package com.yc.java_basic4;

import java.util.Random;
import java.util.Scanner;

public class Wuziqi {
	public static Scanner sc = new Scanner(System.in);
	public static Random r = new Random();
	public static int row = 10;
	public static int col = 10;
	public static int[][] map = new int[row][col];;
	public static boolean isGameOver = false;
	public static int flag = 1;
	public static int[][] score;
	
	
	public static void main(String[] args) {
		System.out.println("欢迎来到简易版五子棋");
		System.out.println("您是黑子,电脑为白子,黑子先下");
		System.out.println("游戏开始");
		
		
		while( !isGameOver ) {
			//首先,先显示棋盘
			show();
			//然后,提示用户,输入要落子的位置
			int x = 0;
			int y = 0;
			System.out.println("请"+ (flag == 1 ? "黑子" : "白子") +"落子");
			if( flag == 1 ) {
				x = sc.nextInt() - 1;
				y = sc.nextInt() - 1;
			}else {
//				x = r.nextInt(row);
//				y = r.nextInt(col);
				
				int[] temp = ai();
				x = temp[0];
				y = temp[1];
			}
			
			
			//得到这个位置后,判断,这个位置,能不能落子,如果不能,则让用户重新输入
			if( x < 0 || x >= row || y < 0 || y >= col || map[x][y] != 0 ) {
				System.out.println("落子失败");
				continue;
			}
			
			//如果能,则将值,变为1   黑子1  白子2
			map[x][y] = flag;
			//每次落子,就有可能游戏胜利
			if(  isWin(x, y) ) {
				show();
				System.out.println("恭喜: " + (flag == 1 ? "黑子" : "白子") + "获得胜利");
				break;
			}
			
			//现在这个游戏,是你自己,和你自己下    应该是,你下完之后,电脑下
			
			//当黑子下完后,应该切换为白子落子   flag 1 -> 2 -> 1 -> 2 -> 1
			flag = flag % 2 + 1;
		}
	}
	
	
	//算法思路： 五元组 一个位置，八个方向，每个方向能够组成一个连续的5个棋子，就称之为一个五元组
	//对10*10的棋盘，来分成N个五元组，然后，根据这个五元组的黑子的数量，和白子的数量，来对这个五元组进行打分(权重)
	//然后，每个五元组的得分，就是每个子的得分
	//最后，将一个点的所有的所属五元组的得分相加，就是这个点的最终得分
	//那么，我们白子要下的棋，就是这个得分最高的点
	public static int[] ai(){
		//每次一开始的时候，得分还原
		score = new int[row][col];
		
		int bnum = 0;	//五元组的黑子的数量
		int wnum = 0;	//五元组的白子的数量
		int temp = 0;	//五元组的得分
		
		int x = -1;
		int y = -1;		//这个x和 y，就是我们白子要落子的位置
		int max = -1;		//最大得分
		
		//现在，开始计算，所有的五元组的得分
		//1、先扫描横向的五元组
		for(int i = 0; i < 10; i++){
			for(int j = 0; j < 6; j++){		//为什么要减4 ？ 因为最后4排，是无法组成五元组的
				int k = j;
				while( k < j + 5 ){		//这是为了确定当前的五元组
					//来计算，这个五元组里面的黑子的数量，和白子的数量
					if( map[i][k] == 1 ){
						bnum++;
					}else if(map[i][k] == 2){
						wnum++;
					}
					k++;
				}
				//来计算得分
				temp = getScore(bnum, wnum);
				//开始将这个五元组的得分，加到每个子上面去
				for(k = j; k < j + 5; k++){
					score[i][k] += temp;
				}
				
				//清零，为下次循环做准备
				bnum = 0;
				wnum = 0;
				temp = 0;
			}
		}
		
		//2、扫描纵向10行
		for(int i = 0; i < 6; i++){
			for(int j = 0; j < 10; j++){		
				int k = i;
				while( k < i + 5 ){		
					if( map[k][j] == 1 ) bnum++;
						else if( map[k][j] == 2 ) wnum++;
						k++;
					}
					temp = getScore(bnum, wnum);
					for(k = i; k < i + 5; k++){
						score[k][j] += temp;
					}
					
					bnum = 0;
					wnum = 0;
					temp = 0;
				}
			}
			
			//3、从右上角，到左下角的上侧部分
			for(int i = 6; i >= 4; i--){   //为什么到4？ 理由同上，要组成五元组
				for(int k = i, j = 0; j < 10 && k >= 0; j++, k--){
					int m = k;
					int n = j;
					while( m > k - 5  &&  k - 5 >= -1 ){		//斜向上
						if( map[m][n] == 1 ) bnum++;
						else if( map[m][n] == 2 ) wnum++;
						
						m--;
						n++;
					}
					
					//注意，因为斜向判断的时候，有可能构不成五元组，因此要判断一下
					if( m == k - 5 ){		//说明上面的循环是循环完毕的
						temp = getScore(bnum, wnum);
						for(m = k, n = j; m > k - 5; m--, n++){
							score[m][n] += temp;
						}
					}
					
					bnum = 0;
					wnum = 0;
					temp = 0;
					
				}
			}
			
			//4、从右上角，到左下角的下侧部分
			for(int i = 1; i < 10; i++){
				for(int k = i, j = 9; j >= 0 && k < 10; j--, k++){
					int m = k;
					int n = j;
					
					while( m < k + 5 && k + 5 <= 10 ){
						if( map[n][m] == 1 ) bnum++;
						else if( map[n][m] == 2 ) wnum++;
						
						m++;
						n--;
					}
					
					if( m == k + 5 ){
						temp = getScore(bnum, wnum);
						for(m = k, n = j; m < k + 5; m++, n--){
							score[n][m] += temp;
						}
					}
					
					bnum = 0;
					wnum = 0;
					temp = 0;
				}
			}
			
			
			//5.扫描左上角到右下角的上侧部分
			for(int i = 0; i < 6; i++){
				for( int k = i, j = 0; j < 10 && k < 10; j++, k++ ){
					int m = k;
					int n = j;
					
					while( m < k + 5  &&  k + 5 <= 10 ){
						if( map[m][n] == 1 ) bnum++;
						else if( map[m][n] == 2 ) wnum++;
						
						m++;
						n++;
					}
					
					if( m == k + 5 ){
						temp = getScore(bnum, wnum);
						for(m = k, n = j; m < k + 5; m++, n++){
							score[m][n] += temp;
						}
					}
					
					bnum = 0;
					wnum = 0;
					temp = 0;
				}
			}
			
			
			//6.扫描左上角到右下角的下侧部分
			for(int i = 1; i < 6; i++){
				for(int k = i, j = 0; j < 10 && k < 10; j++, k++){
					int m = k;
					int n = j;
					
					while( m < k + 5  &&  k + 5 <= 10 ){
						if( map[n][m] == 1 ) bnum++;
						else if( map[n][m] == 2 ) wnum++;
						
						m++;
						n++;
					}
					
					if( m == k + 5 ){
						temp = getScore(bnum, wnum);
						for(m = k, n = j; m < k + 5; m++, n++){
							score[n][m] += temp;
						}
					}
					
					bnum = 0;
					wnum = 0;
					temp = 0;
				}
			}

			
			//假设现在已经做完了，那么，输出测试一下
			/*
			System.out.println("\n");
			for(int i = 0; i < row; i++){
				for(int j = 0; j < col; j++){
					System.out.print(score[i][j] + " ");
				}
				System.out.println();
			}
			System.out.println("\n");
			*/
			
			//将结果返回
			for(int i = 0; i < row; i++){
				for(int j = 0; j < col; j++){
					//保证是空的，才能落子
					if( map[i][j] == 0  &&  score[i][j] > max ){
						x = i;
						y = j;
						max = score[i][j];
					}
				}
			}
			
			
			return new int[]{x, y};
		}
		
		//计算五元组的得分
		public static int getScore(int bnum, int wnum){
			if( bnum > 0 && wnum > 0 ){
				//0、意味着这个五元组，既有黑的，又有白的，那么这个五元组将不会分出胜负，直接返回0
				return 0;
			}
			if( bnum == 0 && wnum == 0 ){
				//1、全部为空，意味着没有落子
				return 7;
			}
			if( wnum == 1 ){
				return 35;
			}
			if( wnum == 2 ){
				return 800;
			}
			if( wnum == 3 ){
				return 15000;
			}
			if( wnum == 4 ){
				return 800000;
			}
			
			//如果你想AI的攻击性高，那么，白子的分值，大于黑子
			//如果你想AI的防守力高，那么，白子的分值，小于黑子
			if( bnum == 1 ){
				return 15;
			}
			if( bnum == 2 ){
				return 400;
			}
			if( bnum == 3 ){
				return 2800;
			}
			if( bnum == 4 ){
				return 100000;
			}
			
			//如果是其他情况，当然，不可能
			return -1;
		}
	
	
	//判断游戏是否胜利
	private static boolean isWin(int x, int y) {
		//只要在4个方向: 横竖撇捺, 连续5个相同的值,就意味着,游戏胜利
		int count = 0;
		//先判断横
		for(int i = y - 4; i <= y + 4; i++) {
			//判断是否超出边界
			if( i < 0 || i >= col ) {
				continue;
			}
			//判断是否是相同的落子
			if( map[x][i] == flag ) {
				count++;
				if( count >= 5 ) {
					return true;
				}
			}else {
				count = 0;			//只要中间有一个不同,都要重新计算
			}
		}
		
		//每次重新验证,count归0
		count = 0;
		for(int i = x - 4; i <= x + 4; i++) {
			if( i < 0 || i >= row ) {
				continue;
			}
			if( map[i][y] == flag ) {
				count++;
				if( count >= 5 ) {
					return true;
				}
			}else {
				count = 0;			
			}
		}
		
		count = 0;
		for(int i = x - 4, j = y - 4; i <= x + 4 && j <= y + 4; i++, j++) {
			if( i < 0 || i >= row || j < 0 || j >= col ) {
				continue;
			}
			if( map[i][j] == flag ) {
				count++;
				if( count >= 5 ) {
					return true;
				}
			}else {
				count = 0;			
			}
		}
		
		count = 0;
		for(int i = x + 4, j = y - 4; i >= x - 4 && j <= y + 4; i--, j++) {
			if( i < 0 || i >= row || j < 0 || j >= col ) {
				continue;
			}
			if( map[i][j] == flag ) {
				count++;
				if( count >= 5 ) {
					return true;
				}
			}else {
				count = 0;			
			}
		}
		
		return false;
	}

	public static void show() {
		System.out.println();
		for(int i = 0; i < map.length; i++) {
			for(int j = 0; j < map[i].length; j++) {
				System.out.print( map[i][j] + " " );
			}
			System.out.println();
		}
	}
}
